
#include "datareader.h"

#include <atlconv.h>
#include <atlcomcli.h>

#include "tinyxml\tinyxml.h"

#include "dataparser.h"

namespace utils_snb
{
	
typedef HRESULT (__stdcall *DllGetClassObjectFunc)(REFCLSID, REFIID, LPVOID*);

CDataReader::CDataReader() : m_bComLoaded(false), m_bFileLoaded(false), m_pExtractor(NULL)
{
	m_hComDll = LoadLibrary(TEXT("snbfilter.dll"));
	if (m_hComDll != NULL)
	{
		DllGetClassObjectFunc fnDllGetClassObject = (DllGetClassObjectFunc)GetProcAddress(m_hComDll, "DllGetClassObject");
		if (fnDllGetClassObject == NULL)
		{
			CloseHandle(m_hComDll);
			m_hComDll = NULL;
			return;
		}

		IClassFactory* pCF = NULL;
		if (FAILED(fnDllGetClassObject(__uuidof(SnbExtractor), IID_IClassFactory, (void**)&pCF)) 
			|| pCF == NULL)
		{
			CloseHandle(m_hComDll);
			m_hComDll = NULL;
			return;
		}

		if (FAILED(pCF->CreateInstance(NULL, __uuidof(ISnbExtractor), (void**)&m_pExtractor)) 
			|| m_pExtractor == NULL)
		{
			pCF->Release();
			CloseHandle(m_hComDll);
			m_hComDll = NULL;
			return;
		}

		pCF->Release();
		m_bComLoaded = true;
	}
}

CDataReader::~CDataReader()
{
	if (m_pExtractor != NULL)
	{
		m_pExtractor->Release();
		m_pExtractor = NULL;
	}

	if (m_hComDll != NULL)
	{
		FreeLibrary(m_hComDll);
		m_hComDll = NULL;
	}

}

bool CDataReader::Load(std::string strFileName)
{
	m_bFileLoaded = false;

	USES_CONVERSION_EX;
	CComBSTR bstrFileName(A2OLE_EX(strFileName.c_str(), 0));

	if (m_bComLoaded && SUCCEEDED(m_pExtractor->Load(bstrFileName)))
	{
		m_strFileName = strFileName;
		m_bFileLoaded = true;
	}

	return m_bFileLoaded;
}

bool CDataReader::GetDesc(SnbDescStructEx_utf8& desc)
{
	std::string strName = "snbf/book.snbf";
	std::string strText;
	if (!GetChapter(strName, strText))
		return false;

	return ParseDesc(strText, desc);
}

bool CDataReader::GetToc(SnbTocStruct_utf8& toc)
{
	std::string strName = "snbf/toc.snbf";
	std::string strText;
	if (!GetChapter(strName, strText))
		return false;

	return ParseToc(strText, toc);
}

bool CDataReader::GetCover(char*& pImage, unsigned int& nImageLength)
{
	std::string strName = "snbc/images/cover.jpg";

	SnbDescStructEx_utf8 desc;
	if (GetDesc(desc) && !desc.strCoverName.empty())
	{
		USES_CONVERSION_EX;
		strName = "snbc/images/";
		strName += W2A_EX(A2W_CP_EX(desc.strCoverName.c_str(), 0, CP_UTF8), 0);
	}

	return GetImage(strName, pImage, nImageLength);
}

bool CDataReader::GetSize(unsigned int& nSize)
{
	if (!m_bComLoaded || !m_bFileLoaded)
		return false;

	if (FAILED(m_pExtractor->GetSize((LPDWORD)&nSize)))
		return false;

	return true;
}

bool CDataReader::GetChapter(const std::string& strName, std::string& strText)
{
	if (!m_bComLoaded || !m_bFileLoaded)
		return false;

	USES_CONVERSION_EX;
	CComBSTR bstrName(A2OLE_EX(strName.c_str(), 0));
	CComVariant varText;

	if (FAILED(m_pExtractor->GetChapter(bstrName, &varText)))
		return false;

	strText = (char *)varText.bstrVal;
	return true;
}

bool CDataReader::GetImage(const std::string& strName, char*& pImage, unsigned int& nImageLength)
{
	if (!m_bComLoaded || !m_bFileLoaded)
		return false;

	USES_CONVERSION_EX;
	CComBSTR bstrName(A2OLE_EX(strName.c_str(), 0));
	CComVariant varImage;

	if (FAILED(m_pExtractor->GetImage(bstrName, &varImage)))
		return false;

	nImageLength = SysStringByteLen(varImage.bstrVal);
	if (nImageLength == 0)
		return false;

	pImage = new char[nImageLength];
	memcpy_s(pImage, nImageLength, varImage.bstrVal, nImageLength);

	return true;
}

bool CDataReader::ParseDesc(const std::string& strText, SnbDescStructEx_utf8& desc)
{
	CXmlDataParser DataParser;
	if (PSNBDATAOBJ pBookSnbf = DataParser.Parse(strText.c_str()))
	{
		if (PSNBDATAOBJ pHead = DataParser.GetObjectByKey("head", pBookSnbf))
		{
			DataParser.GetStringByKey("name",		pHead, desc.strName);
			DataParser.GetStringByKey("author",		pHead, desc.strAuthor);	
			DataParser.GetStringByKey("language",	pHead, desc.strLanguage);
			DataParser.GetStringByKey("right",		pHead, desc.strRight);
			DataParser.GetStringByKey("publisher",	pHead, desc.strPublisher);
			DataParser.GetStringByKey("generator",	pHead, desc.strGenerator);
			DataParser.GetStringByKey("created",	pHead, desc.strCreated);
			DataParser.GetStringByKey("abstract",	pHead, desc.strAbstract);
			DataParser.GetStringByKey("cover",		pHead, desc.strCoverName);

			DataParser.Free(pHead);


			std::string strBookUrl = "snbfile://" + AToUTF8(m_strFileName);
			boost::algorithm::replace_all(strBookUrl, "\\", "/");
			desc.strBookUrl = strBookUrl;

			std::string strCoverUrl = strBookUrl + "?" + desc.strCoverName;
			desc.strCoverUrl = strCoverUrl;
		}

		return true;
	}

	return false;
}

bool CDataReader::ParseToc(const std::string& strText, SnbTocStruct_utf8& toc)
{
	CXmlDataParser DataParser;
	if (PSNBDATAOBJ pTocSnbf = DataParser.Parse(strText.c_str()))
	{
		if (PSNBDATAOBJ pHead = DataParser.GetObjectByKey("head", pTocSnbf))
		{
			LONG nChapters = DataParser.GetLongByKey("chapters", pHead);
			toc.nChapterCount = nChapters;
		}

		if (PSNBDATAOBJ pBody = DataParser.GetObjectByKey("body", pTocSnbf))
		{
			int idx = 0;
			while (true)
			{
				PSNBDATAOBJ pIndex = DataParser.GetObjectByIdx(idx++, pBody);
				if (pIndex == NULL)
					break;

				TiXmlElement* pElement = (TiXmlElement *)pIndex;
				std::string strSrc = pElement->Attribute("src");
				std::string strTitle = pElement->GetText();

				toc.vecChapters.push_back(std::make_pair(strSrc, strTitle));
			}
		}

		DataParser.Free(pTocSnbf);

		return true;
	}

	return false;
}

} // namespace utils_snb